diff --git a/pkg/ipam/hostscope.go b/pkg/ipam/hostscope.go
index 6d58007461..d5446b8df7 100644
--- a/pkg/ipam/hostscope.go
+++ b/pkg/ipam/hostscope.go
@@ -5,8 +5,10 @@ package ipam
 
 import (
 	"fmt"
+	"github.com/cilium/cilium/pkg/k8s/watchers"
 	"math/big"
 	"net"
+	"strings"
 
 	"github.com/cilium/cilium/pkg/ip"
 	"github.com/cilium/cilium/pkg/ipam/service/ipallocator"
@@ -15,9 +17,12 @@ import (
 type hostScopeAllocator struct {
 	allocCIDR *net.IPNet
 	allocator *ipallocator.Range
+
+	// for k8s lister
+	k8swatcher *watchers.K8sWatcher
 }
 
-func newHostScopeAllocator(n *net.IPNet) Allocator {
+func newHostScopeAllocator(n *net.IPNet, k8sEventReg K8sEventRegister) Allocator {
 	cidrRange, err := ipallocator.NewCIDRRange(n)
 	if err != nil {
 		panic(err)
@@ -26,6 +31,7 @@ func newHostScopeAllocator(n *net.IPNet) Allocator {
 		allocCIDR: n,
 		allocator: cidrRange,
 	}
+	a.k8swatcher, _ = k8sEventReg.(*watchers.K8sWatcher)
 
 	return a
 }
@@ -52,12 +58,35 @@ func (h *hostScopeAllocator) Release(ip net.IP, pool Pool) error {
 }
 
 func (h *hostScopeAllocator) AllocateNext(owner string, pool Pool) (*AllocationResult, error) {
-	ip, err := h.allocator.AllocateNext()
+	var result AllocationResult
+	var err error
+	if h.k8swatcher != nil {
+		names := strings.Split(owner, "/")
+		pod, err := h.k8swatcher.GetCachedPod(names[0], names[1])
+		if err != nil {
+			return nil, fmt.Errorf("get pod %s info failed %v. ", owner, err)
+		}
+		if pod.Annotations != nil {
+			if pod.Annotations[customPodIpAddr] != "" {
+				result.IP = net.ParseIP(pod.Annotations[customPodIpAddr])
+				if result.IP == nil {
+					return nil, fmt.Errorf("customer invalid ip: %s. ", pod.Annotations[customPodIpAddr])
+				}
+				err = h.allocator.Allocate(result.IP)
+				if err != nil {
+					return nil, fmt.Errorf("customer ip is not avaliable %s: %w", result.IP.String(), err)
+				}
+				return &result, nil
+			}
+		}
+	}
+
+	result.IP, err = h.allocator.AllocateNext()
 	if err != nil {
 		return nil, err
 	}
 
-	return &AllocationResult{IP: ip}, nil
+	return &result, nil
 }
 
 func (h *hostScopeAllocator) AllocateNextWithoutSyncUpstream(owner string, pool Pool) (*AllocationResult, error) {
diff --git a/pkg/ipam/ipam.go b/pkg/ipam/ipam.go
index 938fc46916..857680d2b1 100644
--- a/pkg/ipam/ipam.go
+++ b/pkg/ipam/ipam.go
@@ -124,11 +124,11 @@ func NewIPAM(nodeAddressing types.NodeAddressing, c Configuration, owner Owner,
 		}).Infof("Initializing %s IPAM", c.IPAMMode())
 
 		if c.IPv6Enabled() {
-			ipam.IPv6Allocator = newHostScopeAllocator(nodeAddressing.IPv6().AllocationCIDR().IPNet)
+			ipam.IPv6Allocator = newHostScopeAllocator(nodeAddressing.IPv6().AllocationCIDR().IPNet, k8sEventReg)
 		}
 
 		if c.IPv4Enabled() {
-			ipam.IPv4Allocator = newHostScopeAllocator(nodeAddressing.IPv4().AllocationCIDR().IPNet)
+			ipam.IPv4Allocator = newHostScopeAllocator(nodeAddressing.IPv4().AllocationCIDR().IPNet, k8sEventReg)
 		}
 	case ipamOption.IPAMClusterPoolV2:
 		log.
diff --git a/pkg/ipam/service/ipallocator/allocator.go b/pkg/ipam/service/ipallocator/allocator.go
index 5e81cf4ee3..782e1e476d 100644
--- a/pkg/ipam/service/ipallocator/allocator.go
+++ b/pkg/ipam/service/ipallocator/allocator.go
@@ -114,18 +114,11 @@ func (r *Range) CIDR() net.IPNet {
 // or has already been reserved.  ErrFull will be returned if there
 // are no addresses left.
 func (r *Range) Allocate(ip net.IP) error {
-	ok, offset := r.contains(ip)
-	if !ok {
-		return &ErrNotInRange{r.net.String()}
-	}
-
-	allocated, err := r.alloc.Allocate(offset)
+	_, offset := r.contains(ip)
+	_, err := r.alloc.Allocate(offset)
 	if err != nil {
 		return err
 	}
-	if !allocated {
-		return ErrAllocated
-	}
 	return nil
 }
 
diff --git a/pkg/ipam/types.go b/pkg/ipam/types.go
index 1e63d38393..95a6e2a58b 100644
--- a/pkg/ipam/types.go
+++ b/pkg/ipam/types.go
@@ -14,6 +14,10 @@ import (
 	"github.com/cilium/cilium/pkg/lock"
 )
 
+const (
+	customPodIpAddr = "cni.cilium.io/ipAddress"
+)
+
 // AllocationResult is the result of an allocation
 type AllocationResult struct {
 	// IP is the allocated IP
